package com.zxjbyte.yiyi.framework.web.filter;

import com.zxjbyte.yiyi.framework.common.constant.Method;
import com.zxjbyte.yiyi.framework.common.util.ServletXUtil;
import com.zxjbyte.yiyi.framework.web.config.YiyiWebProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 包装 HttpServletRequest，使其支持多次读取 InputStream。
 * 仅对可带请求体的请求生效。
 *
 * @Author zhangxingjia
 * @Date 2023/3/17 20:24
 * @Version: 1.0
 */
@Slf4j
@RequiredArgsConstructor
public class YiyiRequestWrapperFilter extends OncePerRequestFilter {

    private final YiyiWebProperties yiyiWebProperties;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (log.isTraceEnabled()) {
            log.trace("[YiyiRequestWrapperFilter] Wrap request for URI: {}", request.getRequestURI());
        }

        // 真正包装 request
        YiyiHttpServletRequestWrapper wrappedRequest = new YiyiHttpServletRequestWrapper(request);
        filterChain.doFilter(wrappedRequest, response);
    }

    /**
     * 返回 true 表示跳过过滤器
     */
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String method = request.getMethod();

        // 仅包装带请求体的方法
        if (!(Method.POST.equals(method) || Method.PUT.equals(method) || Method.PATCH.equals(method) || Method.DELETE.equals(method))) {
            return true;
        }

        // multipart 跳过
        String contentType = request.getContentType();
        if (contentType != null && contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
            return true;
        }

        String path = ServletXUtil.getRequestPath(request);
        // 全局排除路径
        List<String> excludes = yiyiWebProperties.getExcludePaths();
        return ServletXUtil.matchesAnyPattern(path, excludes);
    }
}
